package com.demons.springboot2;

import com.demons.springboot2.demons.web.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import sun.plugin.dom.DOMObjectHelper;

/**@SpringBootApplication是一个合成注解，相当于下面这三个
 * @SpringBootConfiguration
 * @EnableAutoConfiguration
 * @ComponentScan
 */
//@SpringBootApplication
  @SpringBootConfiguration
  @EnableAutoConfiguration
  @ComponentScan("com.demons.springboot2.demons")
  /*
    @SpringBootConfiguration 点进去发现 它实际上就是一个@Configuration 就是一个配置类
    @ComponentScan 指定要扫描哪些
    @EnableAutoConfiguration 这也是一个合成注解 ------>  @AutoConfigurationPackage + @Import(AutoConfigurationImportSelector.class)

        1、@AutoConfigurationPackage 自动配置包里面的注解------> @Import(AutoConfigurationPackages.Registrar.class)

            实际上是导入了Registrar.class  这个类Registrar使用以下两个方法批量导入一系列组件

            public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			    register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));}

			    AnnotationMetadata就是注解的元信息 注解指的就是-----AutoConfigurationPackage 里面记录了一些信息 其中就有它的标注位置
			        其实就是main主类上;
			        new PackageImports(metadata).getPackageNames() 获得了一个包名，此项目就是 com.demons.springboot2
			        然后使用 .toArray(new String[0])封装到一个数组，然后注册出来

	    2、@Import(AutoConfigurationImportSelector.class)

	        1、AutoConfigurationImportSelector类中有这么一个方法 调用getAutoConfigurationEntry(annotationMetadata);批量给容器中注册
                    @Override
                    public String[] selectImports(AnnotationMetadata annotationMetadata) {
                        ...
                    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
                        ...
                    }
            2、getAutoConfigurationEntry(annotationMetadata)这个方法注册了很多组件
                	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
                                ....
                            List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
                                ....
	                        }
                    上面 getCandidateConfigurations(annotationMetadata, attributes);

                       configurations中就有很多 具体可以打断点进行查看
                            getCandidateConfigurations(annotationMetadata, attributes);这个方法点进去会看见

                                protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
                                        ....
                                                SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
                                        ....
                        利用工厂加载
                            SpringFactoriesLoader.loadFactoryNames
                         再追进去有这么一行
                            private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
                                ...
			                    Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			                    ...
                          public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
                          实际上就是jar包中的这个目录，其中spring-boot-autoconfigure-2.7.6.jar中就有这个目录，里面就会配置相关信息

                         虽然启动场景自动配置默认全部加载，实际上会按需配置
                            怎么按需加载，点进去一个类看看 会发现使用了 @Conditional 按需装配

   */
public class Springboot2Application {

    public static void main(String[] args) {
        //1、返回IOC容器
        ConfigurableApplicationContext run =
                SpringApplication.run(Springboot2Application.class, args);

        //2、从容器中获取组件,组件默认就是单实例的
        //如果@Configuration(proxyBeanMethods = true)代理对象调用方法。springboot总会
        //检查这个组件是否是单例
        //保持组件单实例
//        User user = run.getBean("user", User.class);
//        User user2 = run.getBean("user", User.class);
//
//        System.out.println(user==user2);

        //3、获取组件
        String[] beanNamesForType = run.getBeanNamesForType(User.class);
        System.out.println("-------");
        for (String s : beanNamesForType) {
            System.out.println(s);
        }

        DOMObjectHelper bean = run.getBean(DOMObjectHelper.class);
        System.out.println(bean);

        boolean b = run.containsBean("tom");
        System.out.println(b);

        boolean b1 = run.containsBean("haha");
        System.out.println(b1);
    }

}
